Haskell镜头:如何优雅地测试列表项

时间:2018-03-29 07:40:04

标签: haskell lens

query_string

然后我 { "person_name" : "John Bradshaw", "name": "Abraham", "office": { "name":"deVilliers" } }, { "person_name" : "Abraham", "name": "deVilliers", "office": { "name":"blabla" } } 了解上述数据类型。

游戏退出条件是玩家手中的所有牌都是后退。 所以在我的Game StateT monad中,我访问了所有的CardStatus并测试它们。由于我是Lens的新手,我想知道写这篇文章的优雅方式是什么。 在镜头操作员的森林中有点迷失。

2 个答案:

答案 0 :(得分:3)

模块Control.Lens.Fold有许多用于测试镜头/折叠/遍历目标的组合器:has(用于检查棱镜是否匹配),anyOfnoneOfallOf ...

在您的示例中(假设CardStatus也有generated the prisms),我们可以执行以下操作:

endGame :: Game -> Bool
endGame = anyOf (players.folded) (allOf (cards.folded.status) (has _Back))

另外,要找到哪些玩家获胜,我们可以使用filtered

winners :: Fold Game Player
winners = players.folded.filtered (allOf (cards.folded.status) (has _Back))

这些功能类似于典型的列表功能,但可以直接应用于折叠,因此它们不会像镜头世界一样将你拉出来。例如,我们可以继续使用另一个winners撰写Fold

答案 1 :(得分:2)

所以你想要一个能够告诉你所有牌都是Back的镜片。道德上

allBack :: Getter Player Bool

这显然是

的形式
allBack = cards . _

...不要再进一步了,请问GHC是否有任何意义:

$ ghc wtmpf-file11136.hs
wtmpf-file11136.hs:26:19: error:
    • Found hole: _ :: (Bool -> f Bool) -> [Card] -> f [Card]
好吧,听起来很明智。那个签名看起来很可疑是好老的

traverse :: (a -> f b) -> [a] -> f [b]

......这确实是整个Van Laarhoven镜片形式主义的原型,并且在构建实际镜片组合链时经常是有用的。显然,我们仍然需要关注更多,但首先要注意:

allBack = cards . traverse . _

wtmpf-file11136.hs:26:19: error:
    • Could not deduce (Applicative f) arising from a use of ‘traverse’
       ...

wtmpf-file11136.hs:26:30: error:
    • Found hole: _ :: (Bool -> f Bool) -> Card -> f Card

好的,这里的问题是Getter应该立即专注于单个元素。但实际上,我们首先需要检查多个元素以将( fold )压缩为单个Bool。这意味着我们需要将签名从Getter更改为Fold(引擎盖提供Applicative约束):

allBack :: Fold Player Bool
allBack = cards . traverse . _
    • Found hole: _ :: (Bool -> f Bool) -> Card -> f Card
        ...
    • No instance for (Monoid Bool) arising from a use of ‘allBack’

好的,这是有道理的 - 我们指定我们想要以某种方式减少列表,但有一种方法可以减少列表中的bool。在我们的例子中,我们希望它们全部为真,即我们需要从Bool切换到All monoid

import Data.Monoid (All(..))

allBack :: Fold Player All
allBack = cards . traverse . _
wtmpf-file11136.hs:26:30: error:
    • Found hole: _ :: (All -> f All) -> Card -> f Card

好的,那看起来不错。现在我们需要指定我们要检查的卡的属性。好吧,关于它的状态:

allBack = cards . traverse . status . _
    • Found hole: _ :: (All -> f All) -> CardStatus -> f CardStatus

此时我们现在需要做出决定,即我们需要投入 prism 。有人可能会认为它是_Back棱镜,但实际上这代表了“无聊”的情况。我们想要触发失败的情况是_Face

allBack = cards . traverse . status . _Face . _
    • Found hole: _ :: (All -> f All) -> () -> f ()

在此,所有尚未完成的事情是宣布此_Face是失败案例:

allBack = cards . traverse . status . _Face . like (All False)

虽然正如Willem Van Onsem评论说纯粹的镜头在这里并不是最佳选择。将此作为函数写成更为明智,danidiaz' suggestion达到了良好的平衡。