简体   繁体   English

案例类和特征的线性化

[英]Case class and Linearization of traits

Suppose that I want to write a case class Stepper as follows: 假设我想编写一个case类Stepper ,如下所示:

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

It comes with a nice toString implementation: 它带有一个很好的toString实现:

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

but it's not really a function: 但它不是真正的功能:

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

A workaround is to implement the Function trait... 解决方法是实现Function特性...

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

But then, I can't have for free a nice toString implementation anymore: 但是,我不能再免费获得一个很好的toString实现了:

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

Then, the question is: can I have the best of these two worlds? 然后,问题是:我可以充分利用这两个世界吗? Is there a solution where I have the nice toString implementation for free AND an implementation of trait Function . 有没有一个解决方案,我有漂亮toString免费实施特点的实现Function In other words, is there a way to apply the linearization in such a way that case class syntaxic sugar is applied at last? 换句话说,有没有办法以最终应用case class syntaxic sugar的方式应用线性化?

The question is not really to do with linearisation. 这个问题与线性化无关。 In case-classes toString is a method automatically generated by the compiler if and only if Any.toString is not overridden in the end-type. 在case-classes中, toString是一种由编译器自动生成的方法,当且仅当在end-type中没有覆盖Any.toString

However, the answer is partly to do with linearisation - we need to override Function1.toString with the method that would have been generated by compiler if not for the version introduced by Function1 : 但是,答案部分与线性化有关 - 我们需要使用编译器生成的方法覆盖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
}

Then 然后

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

Will produce 会生产

 Some(4) Stepper(2) 

Update 更新

Here is a version of ProperName trait that doesn't rely on the undocumented API method: 以下是ProperName特征的一个版本,它不依赖于未记录的API方法:

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 + "(", ",", ")")
  }
}

Alternative toString implementation is derived from the source code for the original _toString method scala.runtime.ScalaRunTime._toString . 替代toString实现是从原始_toString方法scala.runtime.ScalaRunTime._toString的源代码scala.runtime.ScalaRunTime._toString

Please note that this alternative implementation is still based on the assumption that a case class always extends Product trait. 请注意,此替代实现仍基于以下假设:案例类始终扩展Product trait。 Although the latter holds true as of Scala 2.9.0 and is a fact that is known to and relied upon by some members of Scala community it's not formally documented as part of Scala Language Spec . 虽然后者在Scala 2.9.0中是正确的,并且是Scala社区的一些成员已知和依赖的事实,但它并未作为Scala语言规范的一部分正式记录。

EDIT: What about overriding toString? 编辑:重写toString怎么样?

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

You can use an implicit conversion to have Stepper treated like a function only when necessary: 您可以使用隐式转换将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)
}

Now you get the case class's toString when you call Stepper(42).toString , but Some(2) map Stepper(2) also works as desired. 现在,当调用Stepper(42).toString ,你得到了case类的toString ,但是Some(2) map Stepper(2)也可以按照需要运行。

(Note that I've been more verbose than necessary above to keep the mechanics clear. You can also write implicit def s2f(s: Stepper) = s.apply _ or any number of other more concise formulations). (请注意,为了保持机制清晰,我的上述情况比上面所说的更冗长。您还可以编写implicit def s2f(s: Stepper) = s.apply _或任何其他更简洁的公式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM