Search code examples
haskelllensesmonoidshaskell-lens

Why would my datatype need to an instance of Monoid to use this lens?


I'm using the code below on a record that has a field '_scene' of type SceneGraph. I've created lenses for it using makeLenses.

inputGame :: Input -> Game -> Game
inputGame i g = flip execState g $ do
    let es = g ^. userInput . events
        sg = g ^. scene
    userInput .= i
    scene .= foldl (flip inputEvent) sg es

inputEvent :: InputEvent -> SceneGraph -> SceneGraph
inputEvent (WindowSizeChangedTo (w,h)) (SceneRoot _ _ sg) = SceneRoot w h sg
inputEvent _ sg = sg

I'm getting the error:

No instance for (Monoid SceneGraph) arising from a use of `scene'
Possible fix: add an instance declaration for (Monoid SceneGraph)
In the second argument of `(^.)', namely `scene'
In the expression: g ^. scene
In an equation for `sg': sg = g ^. scene

But I don't understand why SceneGraph has to be an instance of Monoid in order to use this lens.


Solution

  • You probably want either (^?), or maybe (^..) (non-operator names: preview, toListOf).

    When you have a Lens (or a Getter, Iso, Equality, etc.), it always refers to exactly one item. So you can use plain old (^.) (non-operator name: view). When you have have a Traversal (or a Fold, Prism, etc.), it can refer to 0-or-more items.

    Therefore there must be a way to combine them if there's more than one, or a default value to give if there are none. This is done with the Monoid constraint. toListOf gives you a list of all the values; preview gives you either Nothing or Just the first value.

    You didn't give the types for any of the functions you're using, so I can't really tell what you intended. My guess would be that maybe scene can fail because you used makeLenses with a sum type that doesn't define scene in every summand. In this case you'd probably want to use (^?) and handle the Nothing case. But it might be something else.

    See also my answer to this question (and this question from yesterday! This seems to be a popular topic).