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?
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 + ")"
}
You can use an implicit conversion to have Stepper
treated like a function only when necessary:
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.
(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).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.