我无法理解 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
因为这将支持任何
Traversal
或 Setter
,而不仅仅是 Lens
,为您打开更多用例。如果您需要为将来的计算获取值:
:t \p -> p %%= \oldValue -> (whatYouWantToReadFromIt, whatYouWantToWriteToIt)
关于Haskell、透镜、 setter/getter 和二传手,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/20626963/