3
votes

What is the recommended way to access an element of an array within the state monad with lens if the value type is not a monoid.

The following will fail to compile, because lens doesn't know what to do if there is no element at the given index i.

type MyArray = Array Int Char
-- accessElemInStateWrong :: Int -> State MyArray Char
-- accessElemInStateWrong i = use $ ix i

A working version can be implemented by combining gets from Control.Monad.State.Class with preview from Control.Lens.Fold.

accessElemInState :: Int -> State MyArray (Maybe Char)
accessElemInState i = gets $ preview $ ix i

This works just fine. However, given the plethora of functions and operators that lens defines, I was surprised to find that there doesn't seem to be one for this particular case.

So, my question is: Does lens define something like gets . preview? And if not, what's the recommended way to implement accessElementInState?


The reason why I'm asking is because lens does define a special operator outside of the state monad. While the following will not compile for the same reason as above.

-- accessElemWrong :: Int -> MyArray -> Char
-- accessElemWrong i a = a ^. ix i

We can use the operator (^?) to wrap the result in a Maybe and perform safe lookup.

accessElem :: Int -> MyArray -> Maybe Char
accessElem i a = a ^? ix i
1
I’m curious: Why does use $ ix i not work? What is the error message? - Joachim Breitner
@JoachimBreitner The error message is that Char has no instance of Monoid. If the element type is a monoid and we try to access a missing element, then lens will instead return mempty. However, since Char has no monoid instance lens doesn't know what to do in that case. - Lemming

1 Answers

2
votes

There is a function preuse that sounds like exactly what you're looking for:

accessElemInState :: Int -> State MyArray (Maybe Char)
accessElemInState i = preuse $ ix i

-- or
accessElemInState = preuse . ix