使用镜头有哪些优缺点?

时间:2012-05-27 14:15:37

标签: haskell lenses

Lenses似乎没有任何缺点,同时具有优于标准Haskell的显着优势:是否有任何理由我不应该尽可能使用镜头?是否有性能方面的考虑?另外,模板Haskell是否有任何重大开销?

2 个答案:

答案 0 :(得分:25)

镜头构成了对数据构造函数使用直接闭包的替代方法。因此,镜头与直接使用函数和数据构造函数具有大致相同的警告。

因为这一点的一些缺点:

  • 每次修改镜头时,可能会导致(重新)创建大量对象。例如,如果您有此数据结构:

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

    ...以及Lens A String类型的镜头,每当您“修改”该镜头时,您都会创建一个新的ABC。这在Haskell(创建大量对象)中并不罕见,但是对象创建隐藏在镜头后面,因此很难将其视为潜在的性能下沉。

  • 由于使用了“映射功能”,镜头也会造成效率低下。例如,如果您制作的镜头修改了列表中的26 th 元素,则可能会因查找时间而导致大量减速。

和优点:

  • 镜头与常规记录相结合,可以与状态monad一起使用(请参阅data-lens-fd示例),这样就可以避免重新创建大量对象大多数时候,由于广泛的数据共享。例如,请参阅focus函数,以及在Snap Web框架中使用withSomething函数的类似模式。
  • Lenses显然实际上并没有就地修改任何内存,所以当你需要在并发环境中推理状态时它们非常有用。因此,在处理各种图表时,镜头非常有用。
然而,对于数据构造函数的闭包,镜头并不总是同构的。以下是一些差异(将data-lens作为此处的实现):

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

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

答案 1 :(得分:7)

我假设数据镜头包。对于类似数据的事物(记录,元组,地图等),镜头对我来说表现非常好。实际上,它们有时甚至比正常方法表现更好,可能是因为更好的共享。在性能方面,它产生的性能与您手工编写的代码大致相同。

然而,有类似功能的东西,镜头可能会受到惩罚。例如,我至少记得一次使用像这样的镜头:

result :: (Eq a) => a -> Lens (a -> b) b

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