使用 Monocle/Scalaz 镜头时的平等成本

3
我正在阅读关于二极管的内容,这让我想到了Monocle/Scalaz中的透镜:
如果我有一个深度嵌套的数据结构,并使用Monocle/Scalaz透镜条件地修改其中一部分,想要比较是否有更改,是需要进行深度比较,还是可以使用引用相等性来确定两个数据结构(在条件修改之前和之后)是否相同?
换句话说: val f_new=modifyWithMonocleIfTheSunIsUp(f_old)

是否可以使用引用相等性 (eq) 来有效比较数据结构的根 (f_old, f_new),使得比较 f_new==f_old 更加高效?

换句话说,

当且仅当 f_new.eq(f_old) 为真时,f_new==f_old 是否为真?(方程1)

如果不是,为什么?

如果不是,是否有可能使 方程1 成立?如何做到?


根据我的极其有限的经验:如果你关心性能,完全避免使用镜头。它们会严重拖慢你的速度。无论如何,你提出的观点很有趣,期待回答。 - Chobeat
什么是替代方案?替代方案是否更好? - jhegedus
我仍然没有一个好的答案,但认为镜头只是一种具有很少开销的语法糖是一种错误的信念,而人们之所以这样认为是因为它被推销得很好。如果你需要处理大量的类和数据结构,那么它非常好,但对于性能来说,它并不好。但是整个不可变方法对于无法利用并行性的情况是不好的。我对替代方案很感兴趣,因为我可能处于类似您的情境中,我想了解您的问题的答案以获得灵感,或者学习如何更有效地使用镜头或处理嵌套的数据结构。 - Chobeat
我认为,从性能的角度来看,Haskell中的镜头并不差,实际上它们比手动模式匹配更好。这是为什么:https://skillsmatter.com/skillscasts/4251-lenses-compositional-data-access-and-manipulation#video ,所以Scala中的镜头可能也是如此,我认为它们不会比替代方案(即使用复制)更糟糕。 - jhegedus
谢谢提供链接,我会阅读两个。无论如何,我正在做这个:http://stackoverflow.com/questions/41750947/handling-heavy-load-on-the-gc-with-scala-case-classes这是为了让你知道我看到了哪里出现了减速。这个东西已经对GC和处理器来说非常沉重和有压力了。使用镜头时,速度慢了3倍,只用了几个简单的镜头。 - Chobeat
显示剩余4条评论
1个回答

1
也许你可以使用modifyF而不是modify来返回一个Option,这样你就不必检查是否有变化。
例如:
import monocle.Lens
import monocle.macros.GenLens
import scalaz.std.option._

case class Person(name: String, address: Address)
case class Address(street: String, number: Int)

val addressL: Lens[Person, Address] = GenLens[Person](_.address)
val streetL:  Lens[Address, String] = GenLens[Address](_.street)

val changePersonsStreet: Person => Option[Person] =
  (addressL composeLens streetL).modifyF[Option] { street =>
    // only change street name if first letter comes before 'N'
    street.headOption.filter(_.toString.capitalize < "N").map(_ => "New Street")
    // or any other condition
    // if (theSunIsUp) Some("changed street name") else None
  } _

val alice = Person("Alice", Address("Main Street", 1))

val alice2: Option[Person] = changePersonsStreet(alice)
// Some(Person(Alice,Address(New Street,1)))
// -> modified, no need to check

val alice3 = alice2.flatMap(changePersonsStreet)
// None
// -> not modified

谢谢你的回答,这是一个有趣的方法。这个想法是实现快速比较任意两个“人”之间的相等性。因此,如果有人随机给我两个“人”,那么只需要查看引用(使用eq)并且如果它们相等,那么我就知道这两个“人”是相同的(不需要进行深度逐值比较)。采用这种方法,如果有人给我两个随机的Option[Person](在程序的某个时间点生成,但我不知道何时和何地),那么我不能确定它们是否相同。 - jhegedus

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