Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

indexing list with Control.Lens requires Monoid constraint

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.

like image 521
ajp Avatar asked Jul 08 '13 02:07

ajp


1 Answers

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)
like image 74
Thomas M. DuBuisson Avatar answered Sep 27 '22 19:09

Thomas M. DuBuisson