10

我在具有 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 的一个实例才能使用这个镜头。

4

2 回答 2

17

您可能想要(^?),或者可能(^..)(非操作员名称:previewtoListOf)。

当您有 a Lens(或 a GetterIsoEquality等)时,它总是指代一个项目。所以你可以使用普通的旧(^.)(非运营商名称:)view。当您有 a Traversal(或 a FoldPrism等)时,它可以引用 0 个或多个项目。

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

您没有为您正在使用的任何功能提供类型,所以我无法真正说出您的意图。我的猜测是,这可能scene会失败,因为您使用了一个未在每个 summandmakeLenses中定义的 sum 类型。scene在这种情况下,您可能想要使用(^?)和处理该Nothing案例。但它可能是别的东西。

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

于 2013-07-09T03:50:03.823 回答
1

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

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

于 2013-07-09T02:13:29.133 回答