问题
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.
回答1:
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)
来源:https://stackoverflow.com/questions/17518301/indexing-list-with-control-lens-requires-monoid-constraint