【发布时间】:2021-10-28 23:54:22
【问题描述】:
我正在尝试习惯一些基本的lens 功能。在尝试引入镜头之前,我从以下类型和功能开始:
import qualified Data.Set as S
data Sets = Sets {pending, placed, open :: S.Set Int}
interesting :: Sets -> [Int]
interesting = toList . pending <> placed
即,我想要挂起和放置的节点的并集,表示为一个列表(我稍后在列表推导中使用结果,所以一个集合不方便)。
我的基本问题是:我如何使用lens 提供的工具复制它?如果您对该问题有一个好的答案,下面的内容可以跳过;这是我自己对那个空间的初学者探索的记录。
我重命名了字段以给自己镜头:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
import qualified Data.Set as S
data Sets = Sets {_pending, _placed, _open :: S.Set Int}
makeLenses ''Sets
现在希望重新实现interesting。当然,没有镜头也不难(toList . _pending <> _placed),但我正在努力掌握镜头的窍门,这似乎是一个有用的练习。
我的第一个想法是 pending 和 placed 仍然是两个函数,我仍然想逐点映射与其结果相关但并不真正相关的东西,所以 pending <> placed 至少应该很有趣看:
*Main Data.Foldable> :t pending <> placed
pending <> placed
:: (Semigroup (f Sets), Functor f) =>
(S.Set Int -> f (S.Set Int)) -> Sets -> f Sets
现在,这种类型是什么,我可以对它执行哪些操作?它看起来有点像受约束的Getter,也许吧,尽管我无法让 GHCI 通过写:t pending <> placed :: Getter _s _a 告诉我约束是什么。我们可以尝试将它传递给view,它想要一个Getter,这样就可以了:
*Main Data.Foldable> :t view (pending <> placed)
view (pending <> placed) :: MonadReader Sets m => m (S.Set Int)
好吧,这是Sets -> S.Set Int 的概括,我可以用toList 组合它来找回我必须开始的内容:
*Main Data.Foldable> :t toList . view (pending <> placed)
toList . view (pending <> placed) :: Sets -> [Int]
但这似乎不是很令人满意:这只是我以前的情况,但有一个额外的view 电话,我不觉得我在这里使用了任何镜头的力量。我也不太明白 pending <> placed 在这种情况下的“含义”。
我考虑的另一件事是我想做的很像foldMap,而我拥有的有点像Getter,所以我应该可以做一些foldMapOf。
*Main Data.Foldable> :t foldMapOf (pending <> placed)
foldMapOf (pending <> placed)
:: Semigroup r => (S.Set Int -> r) -> Sets -> r
这还需要一个参数,显而易见的候选者是toList:
*Main Data.Foldable> :t foldMapOf (pending <> placed) toList
foldMapOf (pending <> placed) toList :: Sets -> [Int]
这有正确的类型,但是语义不同:它在转换为[Int] 之后使用<>,而不是在底层Set Ints 上,所以如果_pending 和_placed 共享元素,我们得到结果中有重复的副本。
我可以做的另一件事是使用toListOf (pending <> placed),产生一个集合列表,然后使用普通的非镜头函数将它们混合在一起:
*Main Data.Foldable> :t toList . mconcat . toListOf (pending <> placed)
toList . mconcat . toListOf (pending <> placed) :: Sets -> [Int]
这可行,但相当丑陋,似乎没有抓住重点。
那么,镜头在这里给我提供了更好的工具吗?我是否选择了一个如此简单的问题,以至于我看不到镜头相对于简单的记录场吸气剂的优势?
【问题讨论】:
-
我不是镜头专家,但我真的不认为镜头可以为您提供任何东西。每个镜头都可以让您操作
Sets的特定部分,但是您想要做的任何事情来结合这些操作将取决于它们是什么。特别是,由于 Van Laarhoven 镜头(至少)不具有任何正交性,因此没有规范的方法可以将通过两个镜头的编辑组合到相同的结构中。 -
谢谢,@dfeuer。我至少为这种类型的不同操作找到了一个很好的镜头工具:从其中一个集合中删除一个 Int 并将其添加到另一个集合中。即
moveNode idx from to = over from (S.delete idx) . over to (S.insert idx)。 -
当然,但这只是使用一个镜头,然后使用另一个镜头。没有进行合并。
标签: haskell haskell-lens