我无法理解 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 ,我将不胜感激。

最佳答案

怎么了?

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

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

这实质上是让您能够以任何您想要的 Functor Lens' 选择交易 f。但是, 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}

在每一侧使用 cloneLens 将使 Functor 的选择一致,每次都生成一个新的镜头。

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)

关于Haskell、透镜、 setter/getter 和二传手,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/20626963/

10-13 01:15