Haskell,镜头,获取器和设置器

11
我是一名有用的助手,可以为您翻译以下内容:

我很难理解Haskell语言中镜头库的所有细微差别。

假设我拥有以下镜头

activePlayer :: Lens' Game Player
activePlayer = lens get set
    where
        get (Game {_players = (index, seq) }) = S.index seq index
        set g@(Game {_players = (index, seq) }) player =
            g { _players = (index, S.update index player seq) }

在ghci提示符下执行以下操作没有问题:
> :t do{use (activePlayer); activePlayer.= undefined}
     :: Control.Monad.State.Class.MonadState Game m => m ()

然而,当我试着将其参数化为一个函数时,我会得到以下错误。
> :t \p -> do{use p; p.=undefined}
<interactive>:1:17:
    Couldn't match type `Accessor a0 a0' with `Mutator b0'
    Expected type: ASetter s0 s0 a0 b0
      Actual type: Getting a0 s0 a0
    In the first argument of `(.=)', namely `p'
    In a stmt of a 'do' block: p .= undefined
    In the expression:
      do { use p;
           p .= undefined }

看起来p被推断为Accessor,但我希望它被推断为完整的Lens。我尝试使用以下方法强制使p成为一个Lens,但是ghci在Lens' a b中抱怨RankNTypes。

:t \p -> do{use p; p.=undefined} :: Lens' a b -> m c

如果有人能帮我弄清楚为什么p会被推断成现在的样子,以及如何使其表现得像一个完整的Lens,那将不胜感激。


嗯...你试过打开RankNTypes了吗? - Daniel Wagner
1个回答

28

发生了什么?

造成这种情况的原因是,如果您查看Lens'的类型:

type Lens' s a = forall f. Functor f => (a -> f a) -> s -> f s

这基本上给了您在任何一个所选的函子f中交易 Lens' 的能力。然而,use 想要选择 Accessor a,而 .= 想要选择 Mutator

如何做你要求的事情

如果您传递了一个 Lens 并希望多次使用它与不同的函子选择,您需要:

a.) 通过更高等级类型传递它

 {-# LANGUAGE RankNTypes #-}

 foo :: MonadState a m => Lens' a b -> m ()
 foo p = do 
   use p
   p .= undefined

b.) 在使用cloneLens进行读写操作之前,请先复制它。

:t \p -> do{use (cloneLens p); cloneLens p.=undefined}

在每个 side 上使用 cloneLens 将产生一致的 Functor 选择,每次生成一个新的 lens。

c.) 使用 Control.Lens.Loupe 中的组合器。

:t \p -> do gets (^# p); p #= undefined

这些设计旨在始终做出与cloneLens相同类型的选择。

如何实现您可能想要的功能

实际上,最好使用另一种方法,比如

:t \p -> p %= \oldValue -> newValue

作为支持任何TraversalSetter的方法,而不仅仅是Lens,这将为您打开更多用例。
如果您需要在未来进行计算,请获取该值:
:t \p -> p %%= \oldValue -> (whatYouWantToReadFromIt, whatYouWantToWriteToIt)

1
非常感谢!结果使用%=效果更好。我最终还需要使用遍历,而且它运行得非常完美。 - Dwilson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接