8

不変性は多くの人から賞賛されていますが、主流のプログラミングで維持するのは難しいと感じました。私の経験では、プログラマーは遅かれ早かれフィールドを再度変更可能にして、更新されたオブジェクトを戻り値とともに渡す必要がある大きなコードのリファクタリングを回避します。

Scala はコピー コンストラクターをある程度サポートしていますが、複雑なオブジェクト構造を更新するための満足のいく解決策は知りません。私は何かを逃したかもしれません。

私が実験した最良の実装は、Haskell の data-lens です。しかし、Haskell を学ぶのは大変です。Java や Scala などの一般的なクロスプラットフォーム プログラミング言語には、どのようなオプションがありますか?

4

2 に答える 2

7

レンズの言語レベルのサポートは実際には必要ありません。もちろん、言語のプロパティに応じて多かれ少なかれ有用である可能性があり、構文の明快さは言語機能に依存します。

上記のコメントで述べたように、Scala には優れたレンズ ライブラリがありますが、言語自体は提供していません (おそらく提供すべきではありません)。たとえば、次のクラスがあるとします。

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

そしてインスタンス:

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

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 _)

その後:

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

そして今、Foo McBar は自分の電子メール ドメインを簡単に変更できます。

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

これはすべてバニラの 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))

これは、 Aki Saarinenによって開発された概念実証レンズ ライブラリであるRillitを使用します(後に私によって適応されました)。

構文はそれほどきれいではありませんが、これらすべては Java で実行できたはずです。実際、私は見たことも使用したこともありませんが、Java 用のレンズ ライブラリが存在することは確かです。また、不変データ型が比較的重視されていないということは、ほとんどの Java 開発者がレンズを必要としない、または必要としないことを意味します。

于 2013-08-25T16:47:03.487 に答える
0

Octarineにはレンズがあり、レコード キーを構成してデータ構造へのパスを作成するために使用されます。

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")
    ))
)));
于 2014-10-13T10:11:20.420 に答える