镜头似乎没有任何缺点,同时比标准 Haskell 具有显着优势:有什么理由我不应该尽可能使用镜头吗?是否有性能考虑?此外,模板 Haskell 是否有任何重大开销?
问问题
3551 次
2 回答
26
镜头形成了在数据构造函数上使用直接闭包的替代方案。因此,镜头与直接使用函数和数据构造函数具有大致相同的注意事项。
由于这个原因的一些缺点:
每次修改镜头时,都可能会导致(重新)创建大量对象。例如,如果你有这个数据结构:
A { B { C { bla = "foo" } } }
...和类型的镜头
Lens A String
,您将创建一个新的A
,B
并且C
每次您“修改”该镜头。这在 Haskell 中并不少见(创建大量对象),但对象创建隐藏在镜头后面,因此很难将其视为潜在的性能接收器。- 由于使用了“映射功能”,镜头还可能导致效率低下。例如,如果您制作一个修改列表中第 26个元素的镜头,由于查找时间的原因,它可能会导致很多减速。
和优点:
- 镜头与普通记录相结合,可以很好地与状态单子一起使用(参见
data-lens-fd
示例),由于广泛的数据共享,这使得在大多数情况下可以避免重新创建大量对象。例如,参见函数,以及在 Snap Web 框架focus
中使用函数的类似模式。withSomething
- Lenses 显然实际上并没有就地修改任何内存,因此当您需要在并发上下文中推理状态时,它们非常有用。因此,在处理各种图形时,镜头将非常有用。
然而,镜头并不总是与数据构造函数上的闭包同构。以下是一些差异(data-lens
这里作为实现):
- 大多数镜头实现使用某种形式的数据类型将“访问器”和“修改器”存储为一对。因为
data-lens
,它是Store
comonad。这意味着每次创建镜头时,都会因为创建数据结构而产生非常小的额外开销。 - 因为镜头通过一些未知的映射依赖于值,所以对垃圾收集进行推理可能会变得更加困难,并且您可能会遇到(逻辑)内存泄漏,因为您忘记了您正在使用一个非常通用的镜头,该镜头依赖于一些大块的内存。例如,一个镜头访问某个大向量中的元素,该镜头与另一个镜头组合,因此隐藏了第一个镜头,因此很难看出组合镜头仍然依赖于大量内存。
模板 Haskell 代码在编译时运行,并且不会影响镜头的运行时性能。
于 2012-05-27T14:41:05.897 回答
7
我假设data-lens包。Lenses 在数据类事物(记录、元组、地图等)方面对我来说表现得非常好。事实上,它们有时甚至比正常方法表现得更好,可能是因为更好的共享。在性能方面,它产生的性能与您手动编写的代码大致相同。
但是,有一些类似功能的东西,镜头可能会受到惩罚。例如,我记得至少有一次使用过这样的镜头:
result :: (Eq a) => a -> Lens (a -> b) b
虽然查询非常快,但我偶尔会覆盖函数的某些结果值以将其调整到特定场景,这相当于将函数的主体封装在一个大的if
. 当然,性能影响与镜头本身无关,但值得注意。
于 2012-05-27T14:24:51.797 回答