从镜头列表创建遍历
Posted
技术标签:
【中文标题】从镜头列表创建遍历【英文标题】:Create a traversal from a list of lenses 【发布时间】:2021-12-24 15:21:05 【问题描述】:假设我们在数据结构S a
上有一个镜头列表[Lens' (S a) a]
。我想用同样的方法修改数据结构中每个镜头的焦距。
我可以这样做:
s :: S a
s = _
ls :: [Lens' (S a) a]
ls = [a, b, c]
a, b, c = _
f :: a -> a
f = _
s' :: S a
s' = s
& a %~ f
& b %~ f
& c %~ f
没关系,但如果我有 10、100 个镜头怎么办?我想要一些东西 喜欢
s' :: S a
s' = s & ls ??? f
(???) :: *
在哪里找不到运营商(???)
。
也许也可以将ls
转换为遍历并简单地使用(%~)
,我
不知道。
你有什么想法吗?
【问题讨论】:
下面有讨论为什么不能将镜头组合成镜头,以及为什么不能将遍历组合成遍历。但我认为镜头也不能组合成遍历。考虑将(a, b, b)
和(a, b)
之间的两个明显镜头组合起来:以进行类似于&
/%~
链的遍历,对第二个(a, b)
的更改需要通过以下方式观察a
输出第一个变化。这正是Applicative
无法做到的那种观察。
【参考方案1】:
第一个问题是you can't actually define the list ls
as you've done in your question:那个类型是非法的。正如该问题中所讨论的,要将镜头放入容器中,您需要物化镜头。完成此操作后,您可以通过应用 (%~ f)
将每个镜头变成一个函数,然后通过简单的折叠来组合这些函数。
您得到的不是镜头或遍历,而只是S a -> S a
类型的函数(与您从a %~ f
得到的相同)。我发现了a discussion,为什么通常不可能按照您提到的您可能希望的方式将多个镜头、设置器或遍历组合成一个遍历。
这是我上面概述的想法的实现。
data S a = S _x, _y :: a deriving Show
makeLenses ''S
ls :: [ReifiedLens' (S (S Int)) Int]
ls = [Lens (x.x), Lens (y.y)]
overEach :: [ReifiedLens' s a] -> (a -> a) -> (s -> s)
overEach ls f s = foldr applySetter s ls
where applySetter (Lens l) acc = acc & l %~ f
s :: S (S Int)
s = S (S 1 2) (S 3 4)
λ> s & overEach succ ls
S _x = S _x = 2, _y = 2, _y = S _x = 3, _y = 5
【讨论】:
既然 GHC 终于支持了非预测性,那么列出镜头列表不是很好吗? 感谢您的解释和链接。我不知道这是这么复杂。幸运的是,我从变量列表中创建了镜头(我们称它们为索引)。所以现在,我只是折叠索引列表,类似于您的overEach
。然后我避免使用reified lens :)。
@JosephSible-ReinstateMonica 也许吧。当我尝试 OP 的代码时,GHC 没有建议打开语言扩展,它只是告诉我我的类型是非法的。所以我不难看出我是否错过了一些最近的发展。我看到我仍然在 GHC 8.x 上,而在 9.x+ 上是不可预测的。【参考方案2】:
(???) :: [ReifiedLens' (s a) a] -> (a -> a) -> s a -> s a
reLenses ??? f = foldl (.) identity $ map ((%~ f) . runLens) reLenses
将你的镜头包裹在Lens
构造函数中以获得ReifiedLens'
。
【讨论】:
以上是关于从镜头列表创建遍历的主要内容,如果未能解决你的问题,请参考以下文章
函数式编程/光学概念,它采用部分对象并使用镜头和遍历返回“填充”对象?