Rum*_*mca 8 java scala immutability lenses
虽然许多人称赞不变性,但我发现在主流编程中难以维持.根据我的经验,程序员迟早会使字段再次变异,以避免重构大部分代码,这些代码必须传递更新的对象以及返回值.
Scala对复制构造函数有一些支持,但我知道没有令人满意的解决方案来更新复杂的对象结构.我可能错过了什么.
我实验过的最好的实现是Haskell中的数据镜头.但是,Haskell很难学.流行的跨平台编程语言(如Java或Scala)有哪些选择?
实际上不需要语言级别的镜头支持 - 尽管根据语言的属性,它们可能或多或少有用,语法的清晰度将取决于语言功能.
正如我在上面的评论中提到的,即使语言本身没有(并且可以说不应该)提供它们,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开发人员永远不会需要或不需要镜头.