3
votes

I was reading about Diode, and it made me think about lenses in Monocle / Scalaz:

If I (conditionally) modify deeply some part of a deeply nested data-structure using Monocle/Scalaz lens and want to compare if there was a change, is there a need to do a deep comparison, or is there a way to use reference equality to see if the two data-structures (before conditionaly modification and after) are the same ?

In other words:

val f_new=modifyWithMonocleIfTheSunIsUp(f_old)

Can the comparison f_new==f_old be made efficient using reference equality (eq) at the root (f_old, f_new) of the data-structure ?

In other words,

is it true that f_new==f_old is true if and only if f_new.eq(f_old) is true ? (Equation 1)

If not, why not ?

If not, is it possible to make Equation 1 true ? How ?

1
For my extremely limited experience: if you care about performances, avoid lenses entirely. They slow you down a lot. Anyway the point you raise is interesting, waiting for an answer. - Chobeat
What is the alternative ? Is the alternative any better ? - jhegedus
I still have no good answer but believing that lenses are just a form of syntactic sugar with little overhead is a false belief and one is led to think so by how it is sold. It's super good if you need to work with a lot of classes and data structures but for performances, it's not good. But the whole immutable approach is bad for that if you cannot exploit parallelism. I'm interested in alternatives because I'm probably in a scenario similar to your and I would like to know the answer to your question to take inspiration and maybe learn to use lenses more efficiently or to deal with nested DS - Chobeat
I don't think lenses in Haskell, for example, are bad from a performance point of view, actually they are better than the alternative (which is manual pattern matching). This explains why : skillsmatter.com/skillscasts/… , so probably the same is true for lenses in Scala, I don't think they are any worse than the alternative (which is to use copy). - jhegedus
Btw, this is another alternative to lenses : stackoverflow.com/questions/41201045/… , using REFs . - jhegedus

1 Answers

1
votes

Maybe you can use modifyF instead of modify to return an Option so you don't have to check if something has changed.

For example :

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