使用镜片的优缺点是什么?

27

镜片(Lenses) 在具有显着优势的同时似乎没有任何缺点,与标准Haskell相比: 我是否应该尽可能使用镜片? 是否有性能考虑?此外,模板Haskell是否有任何显着的开销?


2
依我之见,目前的Lens包并没有提供足够引人入胜的东西来证明需要增加额外的库依赖和模板Haskell - 相反,我使用Control.Applicative http://www.haskell.org/pipermail/haskell-cafe/2010-September/083543.html。注意 - 我现在通过使用简单的辅助函数来解决嵌套记录更新的问题。 - stephen tetley
2个回答

26
Lenses是使用数据构造函数的替代方法。因此,Lenses具有与直接使用函数和数据构造函数相同的注意事项。
由此带来的一些缺点包括:
  • Every time you modify a lens, you might potentially cause a lot of objects to be (re)created. For example, if you have this data structure:

    A { B { C { bla = "foo" } } }
    

    ...and the lens of type Lens A String, you will create a new A, B and C every time you "modify" that lens. This is nothing unusual in Haskell (creating lots of objects), but the object creation is hidden behind the lens, making it hard to spot as a potential performance sink.

  • A lens could also create inefficiencies due to the "mapping function" being used. For example, if you make a lens that modifies the 26th element in a list, it might cause a lot of slowdowns due to the lookup time.

以下是优点:

  • 透过镜头,结合普通记录,可以与状态单子美妙地搭配使用(请参见data-lens-fd的示例),这使得由于大量数据共享而大部分时间都无需重新创建对象成为可能。例如,请查看focus函数以及在Snap Web框架中使用withSomething函数的类似模式。
  • 显然,镜头不会实际上原地修改任何内存,因此当你需要在并发上下文中理解状态时,它们非常有用。因此,在处理各种图形时,镜头将非常有用。

但是,镜头并不总是等同于对数据构造函数的闭包。以下是一些差异(以data-lens作为此处的实现):

  • 大多数镜头实现使用某种数据类型来存储“访问器”和“变异器”作为一对。对于 data-lens,它是 Store comonad。这意味着每次创建镜头时,由于创建了该数据结构,会有非常小的额外开销。
  • 因为镜头通过某些未知映射依赖于值,所以可能更难推理垃圾收集,并且可能会出现(逻辑)内存泄漏,因为您忘记了您正在使用依赖于某个大块内存的非常通用的镜头。例如,考虑一个访问某个大向量中元素的镜头,它与另一个镜头组合在一起,因此隐藏了第一个镜头,使得很难看到组合的镜头仍然依赖于大量内存。

模板 Haskell 代码在编译时运行,不会影响镜头的运行时性能。


7
我假设您有使用 data-lens 包。透镜在处理数据(记录、元组、映射等)方面表现非常出色。事实上,它们有时甚至比正常的方法表现更好,可能是因为共享效果更好。在性能方面,它产生了与手写代码相当的性能。
然而,对于一些类似函数的东西,透镜可能会产生一些惩罚。例如,我记得至少有一次使用过像这样的透镜:
result :: (Eq a) => a -> Lens (a -> b) b

虽然查询速度非常快,但我偶尔会覆盖函数的某些结果值,以使其适应特定场景,这相当于将函数体包含在一个大的if中。当然,性能影响与镜头本身无关,但这是值得注意的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接