Suppose that I want to write a case class Stepper
as follows:
case class Stepper(step: Int) {def apply(x: Int) = x + step}
It comes with a nice toString
implementation:
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...
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:
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
. In other words, is there a way to apply the linearization in such a way that case class
syntaxic sugar is applied at last?
Unlike class inheritance, in which each class must inherit from just one superclass, a class can mix in any number of traits. Traits are used to define object types by specifying the signature of the supported methods. Scala also allows traits to be partially implemented but traits may not have constructor parameters.
The answer is simple: Case Class can extend another Class, trait or Abstract Class.
A class can extend another class, whereas a case class can not extend another case class (because it would not be possible to correctly implement their equality).
Scala Linearization is a deterministic process which comes into play when an object of a class is created which is defined using inheritance of different traits and classes.
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.
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
:
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:
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
.
Please note that this alternative implementation is still based on the assumption that a case class always extends 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.
EDIT: What about overriding toString?
case class Stepper(step: Int) extends (Int => Int) {
def apply(x: Int) = x + step
override def toString = "Stepper(" + step + ")"
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With