2

I have an array and an array index in a state monad. I can read idx using use and modify it using += and other similar modifiers:

{-# Language TemplateHaskell #-}
import Control.Lens
import Control.Lens.TH
import Control.Monad.State
import Data.Array

data M = M { _arr :: Array Int Int, _idx :: Int }

$(makeLenses ''M)

foo x = do
    idx += x
    ii <- use idx
    return ii

Now I want to combine arr and idx to form a lens to arr[idx]:

combo arr idx = undefined

bar x = do
    combo arr idx += x
    ii <- combo arr idx
    return ii

How can I do this? Will code be different for Data.Sequence?

nponeccop
  • 13,527
  • 1
  • 44
  • 106
  • 1
    I don't know if there's already a suitable lens defined somewhere. Still, it seems that you can build your own lens using the `lens` function and specifying your own getter and setter. – chi Dec 02 '14 at 09:35
  • You'd need to build your own lens for that. Or use something like `use idx >>= \idx -> fmap (!! idx) $ use arr`. – Bartek Banachewicz Dec 02 '14 at 09:53

1 Answers1

0

The answer turned out to be just

combo arr idx f m = (arr . ix (m^.idx)) f m

As the index may be out of bounds, ix is a partial lens called Traversal. So bar has to use uses instead of use:

foo x = do
    combo arr idx += x
    ii <- uses $ combo arr idx
    return ii

Also ii is Monoid m => m Int instead of Int, because of partiality.

If original unsafe behaviour of returning Int is needed it can be restored by replacing uses with unsafeUses traversal = (^?! traversal) <$> get

nponeccop
  • 13,527
  • 1
  • 44
  • 106