11

不変オブジェクトのみを使用してドメインモデルを構築したいと思います。しかし、valフィールドでトレイトを使用し、いくつかの機能をトレイトに移動したいと思います。次の例を見てください。

trait Versionable {
 val version = 0
 def incrementVersion = copy(version=version+1)
}

残念ながら、そのようなコードは機能しません-トレイトVersionableのコピー方法は不明です。

すべての特性とクラスに対してコピーメソッドが生成されると便利だと思います。このようなメソッドは、オブジェクトの浅いコピーを作成し、メソッドに渡された引数に従って指定されたフィールドが変更された元のオブジェクトと同じタイプを使用してそれを返す必要があります。

したがって、次の例では:

class Customer(val name: String) extends Versionable {
 def changeName(newName: String) = copy(name = newName)
}

val customer = new Customer("Scot")

customer.changeName("McDonnald")オブジェクトインスタンスを返す必要がありますCustomer(version = 0, name = "McDonnald")

customer.incrementVersionオブジェクトインスタンスも返す必要がありますCustomer(version = 1, name = "Scot")

私の知る限り、Scalaにはそのような機能が現在ないため、クラスコンストラクターをトレイトフィールドで汚染せずに不変のクラスとトレイトを使用することはできません。私の例では、versionという名前のパラメーターをCustomerクラスに導入したくありません。これは、バージョン処理の機能をVersionableトレイトにカプセル化する必要があるためです。

ケースクラスのコピーメソッドの機能と、デフォルトのパラメータを使用してクラスに独自のコピーメソッドを作成する機能は知っていますが、このようなコピーメソッドを特性で使用することはできないため、この機能では問題は解決しないと思います。既存の機能のもう1つの欠点は、copyメソッドを使用する親クラスが、実際にコピーされるオブジェクトのクラスではなく、親クラスを返すことです。

私の質問:

1)上記の例をエレガントな方法で処理する方法を知っていますか。私はScalaを初めて使用するので、すでに良い解決策があるかもしれません。私の意見では、エレガントなソリューションには次の機能が必要です。

  • リフレクションを使用しないでください

  • シリアル化を使用しないでください

  • 高速である必要があります

  • コンパイル時に検証可能である必要があります

2)上記の例のコピーメソッドのコードを生成するコンパイラプラグインを作成することについてどう思いますか?コンパイラプラグインを使用してそれを行うことは可能ですか?それを行うための例やヒントはありますか?

4

5 に答える 5

8

最もクリーンな解決策は、おそらく からいくつかの実装ロジックを削除Versionableし、型スタックからケース クラス (copyメソッドが使用可能になる場所) にプッシュすることです。バージョン プロパティにデフォルト値を指定して、設計を完了します。

trait Versioned {
  def version : Int
  def nextVersion = version + 1 
}

case class Customer(name: String, version : Int = 0) extends Versioned {
  def withName(newName: String) = copy(name = newName, version = nextVersion)
}

必要に応じて、バージョン番号のどこかに型エイリアスを定義することもできます。

type Version = Int
val initialVersion = 0

trait Versioned {
  def version : Version
  def nextVersion = version + 1 
}

case class Customer(name: String, version : Version = initialVersion)
extends Versioned {
  def withName(newName: String) = copy(name = newName, version = nextVersion)
}
于 2010-09-14T14:05:40.597 に答える
4

OPのコードのように、機能しない別の解決策があります。ただし、言語を拡張するためのより単純な(そしてより一般的には有用な)開始点を提供する場合があります。

trait Versionable[T] {
   self: { def copy(version: Int): T } =>
   val version = 0
   def incrementVersion = copy(version = version + 1)
}

case class Customer(name: String, override val version: Int) 
      extends Versionable[Customer] {
   def changeName(newName: String) = copy(name = newName)
}

コンパイラがCustomerクラスのcopyメソッドをVersionableの自己型アノテーションで定義されたメソッドに準拠していると認識した場合、コードは機能します。これは、名前付きパラメーターとデフォルトパラメーターを使用する自然な方法のようです。

于 2010-08-12T15:56:06.227 に答える
3

あなたは言ったが、ケースクラスを使いたくない。それらを使用したソリューションは次のとおりです。

case class Version(number: Int) {
  override def toString = "v" + number
  def next = copy(number+1)
}

case class Customer(name: String, version: Version = Version(0)) {
  def changeName(newName: String) = copy(newName)
  def incrementVersion = copy(version = version.next)
}

今、あなたはこれを行うことができます:

scala> val customer = new Customer("Scot")
customer: Customer = Customer(Scot,v0)

scala> customer.changeName("McDonnald")
res0: Customer = Customer(McDonnald,v0)

scala> customer.incrementVersion
res1: Customer = Customer(Scot,v1)

scala> customer // not changed (immutable)
res2: Customer = Customer(Scot,v0)
于 2010-08-12T07:28:06.003 に答える
2

これはあなたが探していることをするはずです:

trait Request[T <: Request[T]] extends Cloneable {
  this: T =>
  private var rets = 0
  def retries = rets
  def incRetries:T = {
    val x = super.clone().asInstanceOf[T]
    x.rets = rets + 1
    x
  }
}

次に、次のように使用できます

case class Download(packageName:String) extends Request[Download]
val d = Download("Test")
println(d.retries) //Prints 0
val d2 = d.incRetries
println(d2.retries) //Prints 1
println(d.retries) //Still prints 0   
于 2013-05-02T15:59:45.507 に答える
1

これがどのように機能し、Scala のセマンティクス (特に、トレイトで定義された不変フィールドのセマンティクス) と一致するかを理解するのは困難です。Versionable トレイトを考えてみましょう:

trait Versionable {
   val version = 0
}

versionこの宣言は、オーバーライドされない限り、バージョン フィールドの値が常に 0 になることを示しています。 「クラス コンストラクターを特性フィールドで汚染することなく」(つまり、バージョン フィールドを明示的にオーバーライドせずに)値を変更すると、これらのセマンティクスに違反します。

于 2010-08-11T17:18:31.153 に答える