Which languages support Lenses or similar way to update immutable nested structures?
Asked Answered
M

2

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?

Mensa answered 24/8, 2013 at 23:3 Comment(6)
See Purely Functional Data Structures by Okasaki.Microsome
@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?Kandykane
What about clojure? It can handle immutable nested data structures quite well and runs on the JVM.Nasia
There are several good lens libraries in Scala. See for example Aki Saarinen's Rillit, or my fork of Rillit, which doesn't use Dynamic and does use Shapeless's lens interface, or Scalaz, etc.Capua
@delnan: How do you suppose Okasaki maintains efficiency, if he is not updating nested immutable structures?Microsome
@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: https://mcmap.net/q/348944/-is-there-a-haskell-idiom-for-updating-a-nested-data-structure/395760Kandykane
C
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.

Capua answered 25/8, 2013 at 16:47 Comment(3)
"I'm sure there are lens libraries for Java" -- groundless statement. I haven't found any.Kaikaia
@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.Capua
Hm, I thought "lens" is haskell-specific term and tried only "java haskell lens". Thanks :)Kaikaia
E
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")
    ))
)));
Estovers answered 13/10, 2014 at 10:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.