哪些语言支持镜头或类似的方式来更新不可变的嵌套结构?

Rum*_*mca 8 java scala immutability lenses

虽然许多人称赞不变性,但我发现在主流编程中难以维持.根据我的经验,程序员迟早会使字段再次变异,以避免重构大部分代码,这些代码必须传递更新的对象以及返回值.

Scala对复制构造函数有一些支持,但我知道没有令人满意的解决方案来更新复杂的对象结构.我可能错过了什么.

我实验过的最好的实现是Haskell中的数据镜头.但是,Haskell很难学.流行的跨平台编程语言(如Java或Scala)有哪些选择?

Tra*_*own 7

实际上不需要语言级别的镜头支持 - 尽管根据语言的属性,它们可能或多或少有用,语法的清晰度将取决于语言功能.

正如我在上面的评论中提到的,即使语言本身没有(并且可以说不应该)提供它们,Scala也有很好的镜头库.例如,假设我们有以下类:

case class Email(user: String, domain: String)
case class Contact(email: Email, web: String)
case class Person(name: String, contact: Contact)
Run Code Online (Sandbox Code Playgroud)

一个例子:

val foo = Person(
  "Foo McBar",
  Contact(Email("foo", "mcbar.com"), "http://mcbar.com/foo")
)
Run Code Online (Sandbox Code Playgroud)

使用Shapeless可以编写以下内容(请注意,在即将发布的2.0版本中,不需要同构样板):

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 _)
Run Code Online (Sandbox Code Playgroud)

然后:

val emailDomainLens = Lens[Contact] >> _1 >> _1
Run Code Online (Sandbox Code Playgroud)

现在,Foo McBar可以轻松更改其电子邮件域名:

scala> println(emailHostLens.set(foo)("mcbar.org"))
Person(Foo McBar,Contact(Email(foo,mcbar.com),mcbar.org))
Run Code Online (Sandbox Code Playgroud)

这是所有的香草Scala - 当前版本的Shapeless(1.2.4)不使用宏或编译器插件等,并将适用于Scala 2.9.如果我们愿意使用Scala 2.10的宏,我们可以获得更好的语法和更少的样板:

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))
Run Code Online (Sandbox Code Playgroud)

这使用了Rillit,一个由Aki Saarinen开发的概念验证镜头库(后来由我改编).

所有这些东西都可以用Java完成,尽管语法不太可能干净.事实上,我确信有镜头库用于Java,虽然我从未见过或使用过任何镜头库,并且相对缺乏对不可变数据类型的强调意味着大多数Java开发人员永远不会需要或不需要镜头.

  • @leventov:谷歌搜索"java功能镜头" - 你会在第一页的结果中得到至少几个候选人.我不知道他们是好还是被任何人使用,这就是为什么我没有更积极地说出这句话. (2认同)