Question

While immutability praised by many, I found it hard to maintain in mainstream programming. In my experience, programmers sooner than later will make fields mutable again to avoid refactoring large piece of code that would have to pass updated object along with return value.

Scala has some support with copy constructors but I know no satisfactory solution for updating complex object structures. I might have missed something.

The best implementation I have experimented with is data-lens in Haskell. However, Haskell is tough to learn. What options are there for popular cross-platform programming languages like Java or Scala?

Était-ce utile?

La solution

There's really no need for language-level support for lenses—although of course they may be more or less useful depending on properties of the language, and the clarity of the syntax will depend on language features.

As I mention in a comment above, there are good lens libraries for Scala even though the language itself doesn't (and arguably shouldn't) provide them. For example, suppose we have the following classes:

case class Email(user: String, domain: String)
case class Contact(email: Email, web: String)
case class Person(name: String, contact: Contact)

And an instance:

val foo = Person(
  "Foo McBar",
  Contact(Email("foo", "mcbar.com"), "http://mcbar.com/foo")
)

Using Shapeless you can write the following (note that in the upcoming 2.0 version the isomorphism boilerplate won't be necessary):

import shapeless._, Nat._

implicit val emailIso = Iso.hlist(Email.apply _, Email.unapply _)
implicit val contactIso = Iso.hlist(Contact.apply _, Contact.unapply _)
implicit val personIso = Iso.hlist(Person.apply _, Person.unapply _)

And then:

val emailDomainLens = Lens[Contact] >> _1 >> _1

And now Foo McBar can easily change his or her email domain:

scala> println(emailHostLens.set(foo)("mcbar.org"))
Person(Foo McBar,Contact(Email(foo,mcbar.com),mcbar.org))

This is all vanilla Scala—the current version of Shapeless (1.2.4) doesn't use macros or compiler plugins, etc., and will work on Scala 2.9. If we're willing to use Scala 2.10's macros, we can get even nicer syntax and less boilerplate:

scala> import rillit._
import rillit._

scala> println(Lenser[Person].contact.email.domain.set(foo)("mcbar.org"))
Person(Foo McBar,Contact(Email(foo,mcbar.org),http://mcbar.com/foo))

This uses Rillit, a proof-of-concept lens library developed by Aki Saarinen (and later adapted by me).

All of this stuff could have been done in Java, although the syntax isn't likely to be as clean. In fact I'm sure there are lens libraries for Java, although I've never seen or used any, and the relative lack of emphasis on immutable data types means that most Java developers will never need or want lenses.

Autres conseils

There are lenses in Octarine, which are used to compose record keys to create paths into data structures:

Record testRecord = $$(
        name.of("Arthur Putey"),
        age.of(43),
        address.of(
            addressLines.of("23 Acacia Avenue", "Sunderland", "VB6 5UX")
        )
);

Lens<Record, String> secondLineOfAddress = address.assertPresent()
    .join(addressLines.assertPresent())
    .join(Lens.intoPVector(1));

assertThat(secondLineOfAddress.get(testRecord), equalTo("Sunderland"));
assertThat(secondLineOfAddress.set(testRecord, "Cirencester"), equalTo($$(
    name.of("Arthur Putey"),
    age.of(43),
    address.of(Record.of(
        addressLines.of("23 Acacia Avenue", "Cirencester", "VB6 5UX")
    ))
)));
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top