I'm having trouble understanding all the nuances of the lens library in Haskell.
Suppose I have the following lens
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) }
Performing the following at the ghci prompt works with no problems:
> :t do{use (activePlayer); activePlayer.= undefined}
:: Control.Monad.State.Class.MonadState Game m => m ()
However when I try and parameterize it into a function I get the following error.
> :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 }
It looks like p
is getting inferred as an Accessor
, when I want it to be inferred as a full Lens
. I tried forcing p
to be a Lens
with the following, but ghci complained about RankNTypes in Lens' a b
.
:t \p -> do{use p; p.=undefined} :: Lens' a b -> m c
I would greatly appreciate it if anyone could help me figure out why p
is being inferred the way it is, and how I can make it behave as a full Lens
.
What is happening?
The reason this is happening to you is that if you look at the type of
Lens'
:That is essentially giving you the ability to trade in a
Lens'
at any one choice of Functorf
you want. However,use
wants to pickAccessor a
, while.=
wants to pickMutator
.How to do what you asked
If you are passed a
Lens
and want to use it multiple times with different choices of functor, you'll need to eithera.) pass it with a higher rank type
b.)
cloneLens
it before you use it for reading and/or writing.Using
cloneLens
at each side will make a consistent choice of Functor, generating a fresh lens each time.c.) Use the combinators from
Control.Lens.Loupe
.These are designed to always make the same kind of choice as
cloneLens
would.How to do what you probably want
In practice, it is better to use another approach, like
as this will support any
Traversal
orSetter
, not just aLens
, opening you up to more usecases.If you need to get the value out for future calculations: