haskell 使用隐形眼镜有哪些优点和缺点?

mm9b1k5b  于 2022-12-19  发布在  其他
关注(0)|答案(2)|浏览(140)

Lenses似乎没有任何缺点,但与标准Haskell相比具有显著优势:**是否有任何理由我不应该在任何可能的地方使用镜头?**是否有性能考虑?另外,模板Haskell是否有任何显著的开销?

zi8p0yeb

zi8p0yeb1#

透镜是在数据构造函数上使用直接闭包的一种替代方法,因此透镜与直接使用函数和数据构造函数具有大致相同的警告。
一些缺点,因为这:

  • 每次修改透镜时,都可能导致(重新)创建许多对象。例如,如果您具有以下数据结构:
A { B { C { bla = "foo" } } }

......和Lens A String类型的透镜,每次“修改”该镜头时,您将创建新的ABC。这在Haskell(创建大量对象)中并不罕见,但对象创建隐藏在镜头后面,因此很难发现它是潜在的性能下降点。

  • 透镜也可能会因为使用“Map函数”而导致效率低下。例如,如果你做了一个镜头来修改列表中的第26个元素,它可能会因为查找时间而导致大量的速度下降。

优点:

  • 镜头与普通记录结合使用,可以很好地与状态单子一起使用(参见data-lens-fd的例子),由于广泛的数据共享,这使得在大多数时候 * 避免 * 重新创建大量对象成为可能。参见focus函数的例子,以及在Snap Web框架中使用withSomething函数的类似模式。
  • 很明显,透镜实际上并不修改任何内存,所以当你需要在并发的上下文中推理状态时,透镜非常有用,因此当你处理各种各样的图时,透镜非常有用。

然而,透镜并不总是与数据构造函数上的闭包同构,下面是一些不同之处(这里以data-lens作为实现):

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

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

gev0vcfq

gev0vcfq2#

我假设使用 data-lens 包。Lenses在处理类似数据的东西(记录、元组、Map等)时表现非常好。事实上,它们有时甚至比普通方法表现得更好,可能是因为更好的共享。在性能方面,它产生的性能与手工编写的代码大致相同。
然而,也有一些功能性的东西,镜头可能会有一个惩罚。例如,我记得至少有一次使用这样的镜头:

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

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

相关问题