10

Stepperケースクラスを次のように書きたいとします。

case class Stepper(step: Int) {def apply(x: Int) = x + step}

toString素敵な実装が付属しています:

scala> Stepper(42).toString
res0: String = Stepper(42)

しかし、それは実際には関数ではありません:

scala> Some(2) map Stepper(2)
<console>:10: error: type mismatch;
 found   : Stepper
 required: Int => ?
              Some(2) map Stepper(2)

Function回避策は、特性を実装することです...

case class Stepper(step: Int) extends (Int => Int) {def apply(x: Int) = x + step}

しかし、私はもう素敵な toString 実装を無料で手に入れることはできません:

scala> Stepper(42).toString
res2: java.lang.String = <function1>

次に、問題は、これら 2 つの世界の最高のものを手に入れることができるかということです。toString無料で素敵な実装があり、 trait の実装がある解決策はありますFunction。言い換えれば、case class最終的に構文糖が適用されるような方法で線形化を適用する方法はありますか?

4

3 に答える 3

8

問題は、実際には線形化とは関係ありません。case-classesは、 end-type でオーバーライドされていないtoString場合にのみ、コンパイラによって自動的に生成されるメソッドです。Any.toString

ただし、答えの一部は線形化に関係しています。Function1.toStringによって導入されたバージョンでない場合、コンパイラによって生成されたメソッドでオーバーライドする必要がありますFunction1

trait ProperName extends Product {
  override lazy val toString = scala.runtime.ScalaRunTime._toString(this)
}

// now just mix in ProperName and... magic!
case class Stepper(step: Int) extends (Int => Int) with ProperName {
  def apply(x:Int) = x+step
}

それで

println(Some(2) map Stepper(2))
println(Stepper(2))

生産します

Some(4)
Stepper(2)

アップデート

ProperNameドキュメント化されていない API メソッドに依存しないバージョンの trait を次に示します。

trait ProperName extends Product {
  override lazy val toString  = {
    val caseFields = {
       val arity = productArity
       def fields(from: Int): List[Any] =
         if (from == arity) List()
         else productElement(from) :: fields(from + 1)
       fields(0) 
    }
    caseFields.mkString(productPrefix + "(", ",", ")")
  }
}

代替実装は、元のメソッドtoStringのソース コードから派生します。_toStringscala.runtime.ScalaRunTime._toString

Productこの代替実装は、ケース クラスが常にtraitを拡張するという前提にまだ基づいていることに注意してください。後者は Scala 2.9.0 の時点で当てはまり、Scala コミュニティの一部のメンバーに知られ、信頼されている事実ですが、Scala 言語仕様の一部として正式に文書化されていません。

于 2012-03-14T17:18:40.327 に答える
2

編集: toStringをオーバーライドするのはどうですか?

case class Stepper(step: Int) extends (Int => Int) {
  def apply(x: Int) = x + step
  override def toString = "Stepper(" + step + ")"
}
于 2012-03-14T15:07:33.777 に答える
1

暗黙的な変換を使用してStepper、必要な場合にのみ関数のように扱うことができます。

case class Stepper(step: Int) { def apply(x: Int) = x + step }

implicit def s2f(s: Stepper) = new Function[Int, Int] {
  def apply(x: Int) = s.apply(x)
}

これで、toStringを呼び出したときにケース クラスが取得されますが、必要に応じて機能します。Stepper(42).toStringSome(2) map Stepper(2)

(メカニズムを明確にするために、上記では必要以上に冗長になっていることに注意してください。implicit def s2f(s: Stepper) = s.apply _他のより簡潔な定式化をいくつでも書くことができます)。

于 2012-03-14T16:27:12.993 に答える