使用镜头映射记录的两个字段
Using lenses to mappend two fields of a record
我正在努力适应一些基本的 lens
功能。在尝试介绍镜头之前,我从以下类型和功能开始:
import qualified Data.Set as S
data Sets = Sets {pending, placed, open :: S.Set Int}
interesting :: Sets -> [Int]
interesting = toList . pending <> placed
即,我想要挂起节点和放置节点的并集,表示为列表(我后来在列表理解中使用结果,所以集合不方便)。
我的基本问题是:如何使用 lens
中的工具复制它?如果您对该问题有很好的回答,则可以跳过以下内容;是我自己初学探索的记录 space.
我重命名了这些字段以给自己镜片:
{-# 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
,也许吧,即使我无法通过写 :t pending <> placed :: Getter _s _a
让 GHCI 告诉我约束是什么。我们可以尝试将它传递给 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 Int
上,所以如果 _pending
和 _placed
共享元素,我们在结果中得到重复的副本。
我可以做的另一件事是使用 toListOf (pending <> placed)
,生成一个集合列表,然后使用普通的非镜头函数将它们混合在一起:
*Main Data.Foldable> :t toList . mconcat . toListOf (pending <> placed)
toList . mconcat . toListOf (pending <> placed) :: Sets -> [Int]
这行得通,但是很丑,而且似乎没有抓住要点。
那么,lenses 有什么更好的工具吗?我是否选择了一个如此简单的问题,以至于我看不到透镜相对于简单的记录场吸气剂的优势?
Have I chosen a problem so simple that I can't see the advantage of lenses over simple record-field getters?
我想说的就是这些。直觉上,pending <> 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
Now, what is this type, and what operations can I perform on it? It
looks sorta like a constrained Getter, maybe, even though I can't get
GHCI to tell me what the constraints are by writing :t pending <>
placed :: Getter _s _a.
虽然该类型允许其他一些不太相关的东西,但您真正想要的是 f ~ Const (S.Set Int)
,这使得镜头上的映射实际上映射了检索到的集合。专攻 Const
确实会给你 getter,或者,挑剔的话,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 _ _ _
我正在努力适应一些基本的 lens
功能。在尝试介绍镜头之前,我从以下类型和功能开始:
import qualified Data.Set as S
data Sets = Sets {pending, placed, open :: S.Set Int}
interesting :: Sets -> [Int]
interesting = toList . pending <> placed
即,我想要挂起节点和放置节点的并集,表示为列表(我后来在列表理解中使用结果,所以集合不方便)。
我的基本问题是:如何使用 lens
中的工具复制它?如果您对该问题有很好的回答,则可以跳过以下内容;是我自己初学探索的记录 space.
我重命名了这些字段以给自己镜片:
{-# 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
,也许吧,即使我无法通过写 :t pending <> placed :: Getter _s _a
让 GHCI 告诉我约束是什么。我们可以尝试将它传递给 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 Int
上,所以如果 _pending
和 _placed
共享元素,我们在结果中得到重复的副本。
我可以做的另一件事是使用 toListOf (pending <> placed)
,生成一个集合列表,然后使用普通的非镜头函数将它们混合在一起:
*Main Data.Foldable> :t toList . mconcat . toListOf (pending <> placed)
toList . mconcat . toListOf (pending <> placed) :: Sets -> [Int]
这行得通,但是很丑,而且似乎没有抓住要点。
那么,lenses 有什么更好的工具吗?我是否选择了一个如此简单的问题,以至于我看不到透镜相对于简单的记录场吸气剂的优势?
Have I chosen a problem so simple that I can't see the advantage of lenses over simple record-field getters?
我想说的就是这些。直觉上,pending <> 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
Now, what is this type, and what operations can I perform on it? It looks sorta like a constrained Getter, maybe, even though I can't get GHCI to tell me what the constraints are by writing :t pending <> placed :: Getter _s _a.
虽然该类型允许其他一些不太相关的东西,但您真正想要的是 f ~ Const (S.Set Int)
,这使得镜头上的映射实际上映射了检索到的集合。专攻 Const
确实会给你 getter,或者,挑剔的话,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 _ _ _