8

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?

Rumca
  • 1,809
  • 12
  • 17
  • See [Purely Functional Data Structures](http://www.amazon.com/Purely-Functional-Structures-Chris-Okasaki/dp/0521663504) by Okasaki. – Robert Harvey Aug 24 '13 at 23:07
  • @RobertHarvey I've only read the thesis, but isn't that "only" about efficient purely functional data structures, not about convenient ways of (quoting the question title, as it's probably the most accurate phrasing) updating nested immutable structures? –  Aug 24 '13 at 23:30
  • What about clojure? It can handle immutable nested data structures quite well and runs on the JVM. – Tesseract Aug 25 '13 at 00:09
  • There are several good lens libraries in Scala. See for example Aki Saarinen's [Rillit](https://github.com/akisaarinen/rillit), or [my fork of Rillit](https://github.com/travisbrown/rillit), which doesn't use `Dynamic` and does use [Shapeless](https://github.com/milessabin/shapeless)'s lens interface, or [Scalaz](https://github.com/scalaz/scalaz), etc. – Travis Brown Aug 25 '13 at 00:11
  • @delnan: How do you suppose Okasaki maintains efficiency, if he is not updating nested immutable structures? – Robert Harvey Aug 25 '13 at 03:32
  • 1
    @RobertHarvey They keyword is *convenient*. You might want to read up on lenses. It's not about *efficiently* updating *homogeneous* structures, it's about *conveniently* (i.e. without typing out thirty pattern matches and constructors) updating part of a heterogeneous record within a record within ... within a record: http://stackoverflow.com/q/7365425/395760 –  Aug 25 '13 at 11:18

2 Answers2

7

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.

Travis Brown
  • 138,631
  • 12
  • 375
  • 680
  • "I'm sure there are lens libraries for Java" -- groundless statement. I haven't found any. – leventov Jan 24 '14 at 14:12
  • 2
    @leventov: Do a Google search for `java functional lens`—you'll get at least a couple of candidates on the first page of results. I have no idea whether they're any good or used by anyone, which is why I didn't phrase the statement more positively. – Travis Brown Jan 24 '14 at 14:36
  • Hm, I thought "lens" is haskell-specific term and tried only "java haskell lens". Thanks :) – leventov Jan 24 '14 at 14:59
0

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")
    ))
)));
Dominic Fox
  • 1,039
  • 11
  • 8