我无法理解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
成为Lens' a b
,但是ghci抱怨:t \p -> do{use p; p.=undefined} :: Lens' a b -> m c
中的RankNTypes。
p
如果有人能帮助我弄清楚为什么Lens
被推断出来,以及我如何使其表现为一个完整的{{1}},我将不胜感激。
答案 0 :(得分:26)
发生这种情况的原因是,如果你看一下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
因为这会支持所有Traversal
或Setter
,而不只是Lens
,为您提供更多用例。
如果您需要获取该值以供将来计算:
:t \p -> p %%= \oldValue -> (whatYouWantToReadFromIt, whatYouWantToWriteToIt)