我在具有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的一个实例才能使用这个镜头。
答案 0 :(得分:16)
您可能需要(^?)
或(^..)
(非运营商名称:preview
,toListOf
)。
当您拥有Lens
(或Getter
,Iso
,Equality
等)时,它总是指一个项目。因此,您可以使用普通的旧(^.)
(非运营商名称:view
)。如果您拥有Traversal
(或Fold
,Prism
等),则可以引用0或更多项目。
因此,如果存在多个,则必须有一种方法可以将它们组合,或者如果没有,则必须提供默认值。这是通过Monoid
约束完成的。 toListOf
为您提供所有值的列表; preview
为您提供Nothing
或Just
第一个值。
您没有为您正在使用的任何功能提供类型,因此我无法确切地说出您的意图。我的猜测可能是scene
可能会失败,因为您使用的makeLenses
的和类型在每个加数中都没有定义scene
。在这种情况下,您可能希望使用(^?)
并处理Nothing
案例。但它可能是别的东西。
另请参阅我对this question的回答(以及昨天的this question!这似乎是一个热门话题)。
答案 1 :(得分:1)
我假设您正在使用Ed Kmett的镜头库,如果您还可以发布您正在使用的版本和导入,那将会很有帮助。看起来在该镜头库中支持两个版本的(^。),一个带有Getter,另一个带有折叠,需要Monoid实例的折叠版本:(^。):: Monoid r => s - >折叠s - > [R
编辑:我打赌你不小心导入了Control.Lens.Fold