Scala case class has a default toString function. But when this case class extends a trait with an existing toString() function, it will be rendered useless. How can I prevent this situation?
OK here is the easist answer:
override def toString = ScalaRunTime._toString(this)
end of story:)
Here's a workaround I think may work, it may be too much ceremony, you decide. It involves a trait.
trait StandardToString { this:Product =>
override def toString = productPrefix + productIterator.mkString("(", ",", ")")
}
Now trying it with some samples:
trait Human {
override def toString() = "Human"
}
case class European(firstName:String) extends Human
and running it with the trait:
scala> new European("Falco") with StandardToString
res0: European with StandardToString = European(Falco)
of course with the trait you are left with
scala> new European("Adele")
res1: European = Human
It's more precise to say that the case class toString is not generated, rather than that it is overridden.
This isn't much of an answer or workaround.
scala> trait X { override def toString = "X" }
defined trait X
scala> case class Y(i: Int) extends X
defined class Y
scala> Y(42)
res0: Y = X
scala> case class Y(i: Int)
defined class Y
scala> class Z(x: Int) extends Y(x) with X { override def toString = super[Y].toString }
defined class Z
scala> new Z(42)
res1: Z = Y(42)
You can't do that with a trait:
scala> trait R extends Y { override def toString = super[Y].toString }
<console>:9: error: Implementation restriction: traits may not select fields or methods from super[C] where C is a class
trait R extends Y { override def toString = super[Y].toString }
^
Related
I have the following trait and classes:
trait A{
def func1: String
def func2: Int
}
class F extends A{
def func1: String = ???
def func2: Int = ???
}
class G extends A{
def func1: String = ???
def func2: Int = ???
}
And I want to create the following trait and class:
trait X{
val a: ***
}
class Z extends X{
override val a = new G
}
What do I need to write on '***' in trait X so that I can assign val a with a class F or G ?
Thank you.
EDIT
Sorry, I forgot to mention that trait A is A[Any] and traits F and G can be 'F extends A[String]' and 'G extends A[Int]'. In other words, the classes can extends the trait A with different types of parameters.
Re. your edit, your code fails because trait A only takes type parameter of a fixed type. If you declare base class having a A[Any], you cannot override it with a A[Int] or A[String]. There are two ways to make your code works.
One is to make trait A type argument covariant, so that you can override with subclass of the type argument you declared in base class (note the + in trait A definition):
trait A[+T]
class F extends A[String]
class G extends A[Int]
trait X {
val a: A[Any]
}
class Z extends X {
val a = new G
}
See here: Sample
Another way to work is to declare a as a A[_] (_ means it takes any type as a parameter, which is different from Any):
trait A[T]
class F extends A[String]
class G extends A[Int]
trait X {
val a: A[_]
}
class Z extends X {
val a = new G
}
See here: Sample
I have an implicit class that needs to use a given parameter at runtime. So I define this implicit in another class that takes this parameter in the constructor. A simplified version of what I am doing is as follows:
case class A(p1: String) {
def foo = println("foo: " + p1)
}
class B(p2: String) {
implicit class Enhancer(a: A) {
implicit def bar = s"bar: ${a.p1}, $p2"
}
}
So when I need to use this class I then do the following:
val a = A("x")
val b = new B("y")
import b._
a.bar
I am wondering if there is a neater way than the above? Specifically the middle two lines where I define the object and then import from it. For example is there any way I could have a one line call to return the implicit class I need?
Try to add implicit parameter to Enhancer.
case class A(p1: String) {
def foo = println("foo: " + p1)
}
class B(val p2: String)
implicit class Enhancer(a: A)(implicit b: B) {
implicit def bar = s"bar: ${a.p1}, ${b.p2}"
}
val a = A("x")
implicit object b extends B("y")
a.bar
or
implicit val b = new B("y")
a.bar
Or
implicit class Enhancer(val a: A) extends AnyVal {
implicit def bar(implicit b: B) = s"bar: ${a.p1}, ${b.p2}"
}
I have a trait
trait Weight {
def getWeight: Int
}
Multiple classes inherits it, example:
case class Test(n: Int) extends Weight {
override def getWeight: Int = n
}
Now i want to add sorting ability to all Weight subclasses. I added Ordered to Weight:
trait Weight extends Ordered[Weight] {
def getWeight: Int
override def compare(that: Weight): Int = this.getWeight.compareTo(that.getWeight)
}
Try sorting:
val seq = Seq(Test(1), Test(4), Test(3), Test(2))
seq.sorted // error
And it's not compiles:
Error:(74, 6) diverging implicit expansion for type
scala.math.Ordering[A$A254.this.Test] starting with method $conforms
in object Predef seq.sorted;}
^
Whats i am doing wrong?
Another solution a bit different than mdm. Since sorted takes an implicit of Ordering, you can do the following:
seq.sorted(Ordering[Weight])
Your solution does not work because Ordered[T] is invariant in T, meaning that Ordered[Weight] has no relationship with Ordered[A]. You would need to specify that in the sub-classes.
You could use an implicit Ordering rather than an Ordered.
trait Weight{
def getWeight : Int
}
object Weight{
implicit def ordering[T <: Weight] : Ordering[T] = Ordering.by(w => w.getWeight)
}
case class A(w : Int) extends Weight{
def getWeight = w
}
case class B(w : Int) extends Weight{
def getWeight = w
}
import Weight._
Seq(A(1),B(2),B(0),A(3),A(-3)).sorted
Will result in:
List(A(-3), B(0), A(1), B(2), A(3))
Note that this solution relies on an Ordering[Int] to be available (which is, by default).
It would appear that it is possible to change the implementation of a method on a class with a trait such as follows:
trait Abstract { self: Result =>
override def userRepr = "abstract"
}
abstract class Result {
def userRepr: String = "wtv"
}
case class ValDefResult(name: String) extends Result {
override def userRepr = name
}
val a = new ValDefResult("asd") with Abstract
a.userRepr
Live code is available here: http://www.scalakata.com/52534e2fe4b0b1a1c4daa436
But now I would like to call the previous or super implementation of the function such as follows:
trait Abstract { self: Result =>
override def userRepr = "abstract" + self.userRepr
}
or
trait Abstract { self: Result =>
override def userRepr = "abstract" + super.userRepr
}
However, none of these alternatives compile. Any idea how this could be accomplished?
Here is the answer I was looking for. Thank you Shadowlands for pointing me in the right direction with Scala's abstract override feature.
trait Abstract extends Result {
abstract override def userRepr = "abstract " + super.userRepr
}
abstract class Result {
def userRepr: String = "wtv"
}
case class ValDefResult(name: String) extends Result {
override def userRepr = name
}
val a = new ValDefResult("asd") with Abstract
a.userRepr
Live code is available here: http://www.scalakata.com/52536cc2e4b0b1a1c4daa4a4
Sorry for the confusing example code, I am writing a library that deals with the Scala AST and was not inspired enough to change the names.
I don't know if you are in a position to make the following changes, but the effect you want can be achieved by introducing an extra trait (I'll call it Repr), and using abstract override in the Abstract trait:
trait Repr {
def userRepr: String
}
abstract class Result extends Repr {
def userRepr: String = "wtv"
}
case class ValDefResult(name: String) extends Result {
override def userRepr = name
}
trait Abstract extends Repr { self: Result =>
abstract override def userRepr = "abstract-" + super.userRepr // 'super.' works now
}
Your example usage now gives:
scala> val a = new ValDefResult("asd") with Abstract
a: ValDefResult with Abstract = ValDefResult(asd)
scala> a.userRepr
res3: String = abstract-asd
abstract override is the mechanism, aka stackable traits. It's worth adding that linearization counts, because that's what determines what super means.
This question is a great addendum to the canonical Q&A on self-type vs extension.
Where the inheritance is ambiguous with self-types:
scala> trait Bar { def f: String = "bar" }
defined trait Bar
scala> trait Foo { _: Bar => override def f = "foo" }
defined trait Foo
scala> new Foo with Bar { }
<console>:44: error: <$anon: Foo with Bar> inherits conflicting members:
method f in trait Foo of type => String and
method f in trait Bar of type => String
(Note: this can be resolved by declaring an override in <$anon: Foo with Bar>.)
new Foo with Bar { }
^
Then obviously, you can choose:
scala> new Foo with Bar { override def f = super.f }
res5: Foo with Bar = $anon$1#57a68215
scala> .f
res6: String = bar
scala> new Foo with Bar { override def f = super[Foo].f }
res7: Foo with Bar = $anon$1#17c40621
scala> .f
res8: String = foo
or
scala> new Bar with Foo {}
res9: Bar with Foo = $anon$1#374d9299
scala> .f
res10: String = foo
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 toStringimplementation:
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).