indexing list with Control.Lens requires Monoid constraint
Asked Answered
K

1

9

The following code doesn't compile:

{-# LANGUAGE TemplateHaskell #-}

import Control.Lens

data MyType = MyType Int
data Outer = Outer { _inners :: [ Inner ] }
data Inner = Inner { _val :: MyType }

$(makeLenses ''Outer)
$(makeLenses ''Inner)

i1 = Inner (MyType 1)
i2 = Inner (MyType 2)

o = Outer [i1, i2]

x = o ^. inners . ix 0 . val

giving this error

Toy.hs:17:23:
No instance for (Data.Monoid.Monoid MyType)
  arising from a use of `ix'
Possible fix:
  add an instance declaration for (Data.Monoid.Monoid MyType)
In the first argument of `(.)', namely `ix 0'
In the second argument of `(.)', namely `ix 0 . val'
In the second argument of `(^.)', namely `inners . ix 0 . val'

assuming that it doesn't make sense for MyType to be a monoid, how can I get a Lens (or Traversal, or whatever is most appropriate - I'm not sure of the distinctions) that allows me to access this nested field? Preferably with the ability to both read and update.

Kamerad answered 8/7, 2013 at 2:12 Comment(1)
This question (and my answer) might be relevant here, too.Rubenrubens
D
11

Because ix n can fail (ex: n >= length list) you need a clean way to fail. The clean failure of choice is the mempty element from Monoid. So the question that immediately arises is if your type can't be a Monoid then how would you like this code to fail?

I suggest you use ^? instead of ^., thereby reusing the Monoid named Maybe:

*Main> o ^? inners . ix 2 . val
Nothing
*Main> o ^? inners . ix 0 . val
Just (MyType 1)
Discomfortable answered 8/7, 2013 at 2:42 Comment(1)
As a small note, the Monoid instance being used here is First a, not Maybe a (since we don't have enough type class hierarchy to talk about affine traversals).Rubenrubens

© 2022 - 2024 — McMap. All rights reserved.