从镜头列表创建遍历

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'

【讨论】:

以上是关于从镜头列表创建遍历的主要内容,如果未能解决你的问题,请参考以下文章

函数式编程/光学概念,它采用部分对象并使用镜头和遍历返回“填充”对象?

非法镜头 Haskell

使角色从镜头中逃脱

有关镜头F值

游戏开发实战2D游戏摄像机镜头跟随,屏幕边缘限制镜头移动(使用Cinemachine组件)

游戏开发实战2D游戏摄像机镜头跟随,屏幕边缘限制镜头移动(使用Cinemachine组件)