【问题标题】:Using lenses to mappend two fields of a record使用镜头映射记录的两个字段
【发布时间】: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 &lt;&gt; _placed),但我正在努力掌握镜头的窍门,这似乎是一个有用的练习。

我的第一个想法是 pendingplaced 仍然是两个函数,我仍然想逐点映射与其结果相关但并不真正相关的东西,所以 pending &lt;&gt; 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 &lt;&gt; placed :: Getter _s _a 告诉我约束是什么。我们可以尝试将它传递给view,它想要一个Getter,这样就可以了:

*Main Data.Foldable> :t view (pending <> placed)
view (pending <> placed) :: MonadReader Sets m => m (S.Set Int)

好吧,这是Sets -&gt; S.Set Int 的概括,我可以用toList 组合它来找回我必须开始的内容:

*Main Data.Foldable> :t toList . view (pending <> placed)
toList . view (pending <> placed) :: Sets -> [Int]

但这似乎不是很令人满意:这只是我以前的情况,但有一个额外的view 电话,我不觉得我在这里使用了任何镜头的力量。我也不太明白 pending &lt;&gt; 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] 之后使用&lt;&gt;,而不是在底层Set Ints 上,所以如果_pending_placed 共享元素,我们得到结果中有重复的副​​本。

我可以做的另一件事是使用toListOf (pending &lt;&gt; 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


【解决方案1】:

我是否选择了一个如此简单的问题,以至于我看不到镜头相对于简单的记录场吸气剂的优势?

我想说,基本上就是这样。直观地说,pending &lt;&gt; placed 是一个只读目标:作为Sets 结构的一部分,没有明智的方法可以修改这两个集合的并集,因为它不对应于其中的任何内容。这就是为什么你最终会得到一个 getter,正如你所发现的,它本质上是一个函数。

*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 告诉我有什么限制 放置 :: Getter _s _a。

虽然该类型允许一些其他不太相关的东西,但您真正想要的是f ~ Const (S.Set Int),这使得镜头上的映射实际上映射检索到的集合。专注于Const 确实给了你一个吸气剂,或者,挑剔的是a Getting:t 稍微有点帮助:

ghci> :t pending <> placed :: Getting _ _ _

<interactive>:1:34: error:
    • Found type wildcard ‘_’ standing for ‘S.Set Int’
      To use the inferred type, enable PartialTypeSignatures
    • In the third argument of ‘Getting’, namely ‘_’
      In the type ‘Getting _ _ _’
      In an expression type signature: Getting _ _ _

<interactive>:1:32: error:
    • Found type wildcard ‘_’ standing for ‘Sets’
      To use the inferred type, enable PartialTypeSignatures
    • In the second argument of ‘Getting’, namely ‘_’
      In the type ‘Getting _ _ _’
      In an expression type signature: Getting _ _ _

<interactive>:1:30: error:
    • Found type wildcard ‘_’ standing for ‘_’
      Where: ‘_’ is a rigid type variable bound by
               the inferred type of
                 it :: Semigroup _ => Getting _ Sets (S.Set Int)
               at <interactive>:1:1
      To use the inferred type, enable PartialTypeSignatures
    • In the first argument of ‘Getting’, namely ‘_’
      In the type ‘Getting _ _ _’
      In an expression type signature: Getting _ _ _

【讨论】:

    猜你喜欢
    • 2015-04-06
    • 1970-01-01
    • 2012-06-21
    • 1970-01-01
    • 2012-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-21
    相关资源
    最近更新 更多