为什么我的数据类型需要使用Monoid的实例来使用这个镜头?

时间:2013-07-09 01:47:11

标签: haskell lenses monoids lens

我在具有SceneGraph类型字段'_scene'的记录上使用下面的代码。我使用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

我收到了错误:

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

但我不明白为什么SceneGraph必须是Monoid的一个实例才能使用这个镜头。

2 个答案:

答案 0 :(得分:16)

您可能需要(^?)(^..)(非运营商名称:previewtoListOf)。

当您拥有Lens(或GetterIsoEquality等)时,它总是指一个项目。因此,您可以使用普通的旧(^.)(非运营商名称:view)。如果您拥有Traversal(或FoldPrism等),则可以引用0或更多项目。

因此,如果存在多个,则必须有一种方法可以将它们组合,或者如果没有,则必须提供默认值。这是通过Monoid约束完成的。 toListOf为您提供所有值的列表; preview为您提供NothingJust第一个值。

您没有为您正在使用的任何功能提供类型,因此我无法确切地说出您的意图。我的猜测可能是scene可能会失败,因为您使用的makeLenses的和类型在每个加数中都没有定义scene。在这种情况下,您可能希望使用(^?)并处理Nothing案例。但它可能是别的东西。

另请参阅我对this question的回答(以及昨天的this question!这似乎是一个热门话题)。

答案 1 :(得分:1)

我假设您正在使用Ed Kmett的镜头库,如果您还可以发布您正在使用的版本和导入,那将会很有帮助。看起来在该镜头库中支持两个版本的(^。),一个带有Getter,另一个带有折叠,需要Monoid实例的折叠版本:(^。):: Monoid r => s - >折叠s - > [R

编辑:我打赌你不小心导入了Control.Lens.Fold